package midend.IR_Instruction;

import midend.IR_Type.IR_Type;
import midend.IR_Value.*;

import java.util.*;

public class Phi_Instruction extends Instruction {

    private final LinkedHashMap<IR_BaseBlock, IR_Value> comingValue;
    private LinkedList<IR_Value> valueList;
    private LinkedList<IR_BaseBlock> preBlockList;
    public Phi_Instruction(List<IR_Value> ops, IR_BaseBlock bb) {
        super(ops, bb);
        this.comingValue = new LinkedHashMap<>();
        valueList = new LinkedList<>();
        preBlockList = new LinkedList<>();
    }

    public LinkedHashMap<IR_BaseBlock, IR_Value> get_ComingValues() { return this.comingValue; }

    public List<IR_Value> getValueList() { return this.valueList; }

    public void set_ComingValue(IR_Value v, IR_BaseBlock bb) {
        this.comingValue.put(bb, v);
        if(preBlockList.contains(bb)){
            int index = preBlockList.indexOf(bb);
            valueList.set(index,v);
            setValue(index,v);
        }
        else {
            valueList.add(v);
            preBlockList.add(bb);
            addValue(v);
        }
    }

    public void updateop(int index, IR_Value newValue) {
        IR_Value oldValue = getUserValue(index);
        //IR_BaseBlock bb = comingValue.get(oldValue);
        int idx = valueList.indexOf(oldValue);
        if (idx == -1) {
            System.out.println("phi 错误");
            return;
        }
        IR_BaseBlock bb = preBlockList.get(idx);
        if (comingValue.get(bb) != oldValue) {
            System.out.println("phi 指令数据和块对应错误");
        }

        int inx = preBlockList.indexOf(bb);
        setValue(inx,newValue);

        remove_ComingValues(bb);
        //set_ComingValue(newValue, bb);
        this.comingValue.put(bb, newValue);
        valueList.add(newValue);
        preBlockList.add(bb);

    }

    public void remove_ComingValues(IR_BaseBlock bb) {
        if(this.comingValue.containsKey(bb)) {
            this.preBlockList.remove(bb);
            this.valueList.remove(comingValue.get(bb));
            this.comingValue.remove(bb);
        }
    }

    public IR_Value getValue(int id) {
        return valueList.get(id);
    }

    public IR_BaseBlock getPreBlock(int count) {
        return preBlockList.get(count);
    }

    public IR_BaseBlock getPreBlock(IR_Value value) {
        for (int count = 0; count < preBlockList.size(); count++) {
            if (getValue(count).equals(value)) {
                return preBlockList.get(count);
            }
        }
        return null;
    }


    public void change_Origin(IR_BaseBlock before, IR_BaseBlock after) {
        for (int count = 0; count < this.preBlockList.size(); count++) {
            if (this.preBlockList.get(count).equals(before)) {
                this.preBlockList.set(count, after);
                IR_Value value = valueList.get(count);
                comingValue.remove(before);
                set_ComingValue(value, after);
                break;
            }
        }
    }

    //public IR_Value get_ComingValue(IR_BaseBlock bb) {}
    @Override
    public String toString() {
        // op [ans]
        StringBuilder sb = new StringBuilder();
        IR_Value res = get_op().get(0);
        sb.append(res.get_ValueName()).append(" = phi ").append(res.get_Type().get_TypeName()).append(" ");
        boolean flag = true;
        for (Map.Entry<IR_BaseBlock, IR_Value> entry : comingValue.entrySet()) {
            if(!flag) {
                sb.append((", "));
            }
            sb.append("[ ").append(entry.getValue().get_ValueName()).append(", %").append(entry.getKey().get_ValueName()).append(" ]");
            flag = false;
        }
        return sb.toString();
    }

    @Override
    public Phi_Instruction clone(IR_BaseBlock baseBlock) {
        ArrayList<IR_Value> copy_ops = new ArrayList<>();
        String copy_name = ((IR_VirtualReg_Value) get_op().get(0)).getIdentity();
        IR_Type copy_type = get_op().get(0).get_Type();
        IR_Value res = new IR_VirtualReg_Value(copy_name, copy_type);
        copy_ops.add(res);
        Phi_Instruction phiInstruction = new Phi_Instruction(copy_ops, baseBlock);
        return phiInstruction;
    }

}
